/**
  ******************************************************************************
  * @author     Chris
  * @since      2023/10/11 22:31
  *
  * @file       hd_lcd.c
  * @brief      TFT LCD Hardware Driver.
  *
  * @note       This file contains the hardware driver for the TFT LCD.
  *
  * @warning    None.
  ******************************************************************************
  * Change Logs:
  *   Date           Author       Notes
  *   2023/10/11     Chris        the first version
  *
  ******************************************************************************
  */
#include "hd_lcd.h"
#include "hd_lcd_font.h"
#include "delay.h"
#include "stm32g4xx_ll_gpio.h"
#include "stm32g4xx_ll_bus.h"


/** region Declaration and Definition Region */

/** region Variables and Constants Region */

/** region LCD 初始化指令表 */
typedef struct {
    uint8_t reg;
    uint8_t len;
    uint8_t dat[16];
} HD_LCD_InitCmd;
const HD_LCD_InitCmd HD_LCD_INIT_CMD[] = {
        {0xB1, 3, {0x05, 0x3C, 0x3C}},  // Normal Mode
        {0xB2, 3, {0x05, 0x3C, 0x3C}},  // Idle Mode
        {0xB3, 6, {0x05, 0x3C, 0x3C, 0x05, 0x3C, 0x3C}},    // Partial Mode
        {0xB4, 1, {0x03}},                      // Dot Inversion
        {0xC0, 3, {0xAB, 0x0B, 0x04}},  // AVDD GVDD
        {0xC1, 1, {0xC5}},               // VGH VGL
        {0xC2, 2, {0x0D, 0x00}},    // Normal Mode
        {0xC3, 2, {0x8D, 0x6A}},    // Idle
        {0xC4, 2, {0x8D, 0xEE}},    // Partial + Full
        {0xC5, 1, {0x0F}},               // VCOM
        {0xE0, 16, {0x07, 0x0E, 0x08, 0x07, 0x10, 0x07, 0x02, 0x07, 0x09, 0x0F, 0x25, 0x36, 0x00, 0x08, 0x04,
                    0x10}},   // Positive Gamma
        {0xE1, 16, {0x0A, 0x0D, 0x08, 0x07, 0x0F, 0x07, 0x02, 0x07, 0x09, 0x0F, 0x25, 0x35, 0x00, 0x09, 0x04,
                    0x10}},   // Negative Gamma
        {0xFC, 1, {0x80}},              //
        {0x3A, 1, {0x05}},              //
#if USE_HORIZONTAL == 0
        {0x36, 1, {0x08}},
#elif USE_HORIZONTAL == 1
        {0x36, 1, {0xC8}},
#elif USE_HORIZONTAL == 2
        {0x36, 1, {0x78}},
#else
        {0x36, 1, {0xA8}},
#endif
        {0x21, 0, {0}},   // Display Inversion
        {0x29, 0, {0}},   // Display On
        {0x2A, 4, {0x00, 0x1A, 0x00, 0x69}},    // Set Column Address
        {0x2B, 4, {0x00, 0x01, 0x00, 0xA0}},    // Set Page Address
        {0x2C, 0, {0}}
};
/** endregion LCD 初始化指令表 */


/** region LCD 端口定义 */
#define HD_LCD_SCK(x)   HD_LCD_WriteBit(GPIOA, LL_GPIO_PIN_9, x)    // SCL、SCLK、SCK
#define HD_LCD_SDA(x)   HD_LCD_WriteBit(GPIOA, LL_GPIO_PIN_8, x)    // SDA(MOSI)
#define HD_LCD_RES(x)   HD_LCD_WriteBit(GPIOG, LL_GPIO_PIN_10, x)   // RES：复位
#define HD_LCD_DC(x)    HD_LCD_WriteBit(GPIOB, LL_GPIO_PIN_13, x)   // DC：数据（1）或指令（0）
#define HD_LCD_CS(x)    HD_LCD_WriteBit(GPIOB, LL_GPIO_PIN_12, x)   // CS：片选
#define HD_LCD_BLK(x)   do{}while(0)                                // BLK：背光
/** endregion LCD 端口定义  */

uint8_t fontSize = 16;
uint8_t fontMode = 0;
uint16_t fontColor = BLACK;
uint16_t fontBgColor = WHITE;

/** endregion Variables and Constants Region */

/** region Function Declaration Region */

void HD_LCD_WriteReg(uint8_t regAddr);

void HD_LCD_WriteData8(uint8_t dat);

void HD_LCD_WriteData16(uint16_t dat);

void HD_LCD_SetAddress(uint16_t x, uint16_t y, uint16_t x1, uint16_t y1);

void HD_LCD_WriteByte(uint8_t dat);

void HD_LCD_WriteBit(GPIO_TypeDef *GPIOx, uint16_t Pin, FlagStatus val);

uint32_t HD_LCD_Pow(uint8_t m, uint8_t n);

uint8_t HD_LCD_GetASCII(uint8_t x, uint16_t y);

/** endregion Function Declaration Region */

/** endregion Declaration and Definition Region*/


/** region Public Definition Region */

/**
 * @brief   TFT LCD 初始化
 *
 * @note    1. GPIO 初始化
 *          2. LCD 发送初始化指令
 * @param   无
 * @retval  无
 */
void HD_LCD_Init(void) {
    // 使能 PA、PB 时钟
    LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA | LL_AHB2_GRP1_PERIPH_GPIOB);

    // PA8、PA9
    LL_GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.Pin = LL_GPIO_PIN_8 | LL_GPIO_PIN_9;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
    GPIO_InitStruct.Alternate = LL_GPIO_AF_0;
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    LL_GPIO_WriteOutputPort(GPIOA, LL_GPIO_PIN_8 | LL_GPIO_PIN_9);

    // PB12、PB13
    GPIO_InitStruct.Pin = LL_GPIO_PIN_12 | LL_GPIO_PIN_13;
    LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    LL_GPIO_WriteOutputPort(GPIOB, LL_GPIO_PIN_12 | LL_GPIO_PIN_13);
    HD_LCD_CS(SET);
    HD_LCD_DC(SET);

    // 复位
    HD_LCD_RES(RESET);
    delay_ms(100);
    HD_LCD_RES(SET);
    delay_ms(100);
    // 打开背光（默认打开）
    HD_LCD_BLK(SET);
    delay_ms(100);
    // Sleep out
    HD_LCD_WriteReg(0x11);
    delay_ms(120);
    // 其它配置
    for (int i = 0; i < sizeof(HD_LCD_INIT_CMD) / sizeof(HD_LCD_InitCmd); ++i) {
        HD_LCD_WriteReg(HD_LCD_INIT_CMD[i].reg);
        for (int j = 0; j < HD_LCD_INIT_CMD[i].len; ++j) {
            HD_LCD_WriteData8(HD_LCD_INIT_CMD[i].dat[j]);
        }
    }

    // 填充颜色
    HD_LCD_Fill(0, 0, LCD_W, LCD_H, fontBgColor);
}

/**
 * @brief   TFT LCD 填充指定区域
 *
 * @note    无
 * @param   x   起始横坐标
 * @param   y   起始纵坐标
 * @param   x1  结束横坐标
 * @param   y1  结束纵坐标
 * @param   color   填充颜色
 * @retval  无
 */
void HD_LCD_Fill(uint16_t x, uint16_t y, uint16_t x1, uint16_t y1, uint16_t color) {
    // 设置显示范围
    HD_LCD_SetAddress(x, y, x1 - 1, y1 - 1);

    for (uint16_t i = y; i < y1; i++) {
        for (uint16_t j = x; j < x1; j++) {
            HD_LCD_WriteData16(color);
        }
    }
}

/**
 * @brief   TFT LCD 设置字体大小
 *
 * @note    无
 * @param   size 待设置的字体大小，可以取以下值：
 *          @arg @ref HD_LCD_FONT_SIZE_12
 *          @arg @ref HD_LCD_FONT_SIZE_16
 *          @arg @ref HD_LCD_FONT_SIZE_24
 *          @arg @ref HD_LCD_FONT_SIZE_32
 * @retval  无
 */
void HD_LCD_SetFontSize(uint8_t size) {
    fontSize = size;
}

/**
 * @brief   TFT LCD 设置字体显示模式
 *
 * @note    无
 * @param   mode 待设置的字体模式，可以取以下值：
 *          @arg @ref HD_LCD_FONT_MODE_OVERLAY
 *          @arg @ref HD_LCD_FONT_MODE_NO
 * @retval  无
 */
void HD_LCD_SetFontMode(uint8_t mode) {
    fontMode = mode;
}

/**
 * @brief   TFT LCD 设置字体前景色
 *
 * @note    无
 * @param   color 待设置的字体前景色，可以取以下值：
 *          @arg @ref WHITE
 *          @arg @ref BLUE
 *          @arg @ref RED
 *          @arg @ref GREEN
 *          (*) 未全部列出.
 * @retval  无
 */
void HD_LCD_SetFontColor(uint16_t color) {
    fontColor = color;
}

/**
 * @brief   TFT LCD 设置字体背景色
 *
 * @note    无
 * @param   color 待设置的字体背景色，可以取以下值：
 *          @arg @ref WHITE
 *          @arg @ref BLUE
 *          @arg @ref RED
 *          @arg @ref GREEN
 *          (*) 未全部列出.
 * @retval  无
 */
void HD_LCD_SetFontBgColor(uint16_t color) {
    fontBgColor = color;
}


/** region 显示 ASCII */

/**
 * @brief   TFT LCD 显示单个字符
 *
 * @note    无
 * @param   x   起始横坐标
 * @param   y   起始纵坐标
 * @param   chr 待显示的字符
 * @retval  无
 */
void HD_LCD_ShowChar(uint16_t x, uint16_t y, uint8_t chr) {
    const uint16_t baseX = x;
    const uint16_t displayBytes = (fontSize / 2 / 8 + ((fontSize / 2 % 8) ? 1 : 0)) * fontSize;  // 一个字符所占字节大小
    uint8_t m = 0;
    HD_LCD_SetAddress(x, y, x + fontSize / 2 - 1, y + fontSize - 1);  // 设置光标位置
    for (uint16_t i = 0; i < displayBytes; i++) {
        uint8_t temp = HD_LCD_GetASCII(chr - ' ', i); // 根据设置的字体获取字节
        for (uint8_t j = 0; j < 8; j++) {
            // 叠加模式：一个像素点要么显示字体色，要么显示背景色
            if (fontMode == 0) {
                if (temp & (0x01 << j)) { HD_LCD_WriteData16(fontColor); }
                else { HD_LCD_WriteData16(fontBgColor); }
                m++;
                if (m % (fontSize / 2) == 0) {
                    m = 0;
                    break;
                }
            } else {
                // 非叠加模式：像素点只显示字体颜色
                if (temp & (0x01 << j)) HD_LCD_DrawPoint(x, y, fontColor);
                x++;
                if ((x - baseX) == fontSize / 2) {
                    x = baseX;
                    y++;
                    break;
                }
            }
        }
    }
}

/**
 * @brief   TFT LCD 显示字符串
 *
 * @note    无
 * @param   x   起始横坐标
 * @param   y   起始纵坐标
 * @param   str 待显示的字符
 * @retval  无
 */
void HD_LCD_ShowString(uint16_t x, uint16_t y, const uint8_t *str) {
    while (*str != '\0') {
        HD_LCD_ShowChar(x, y, *str);
        x += fontSize / 2;
        str++;
    }
}

/**
 * @brief   TFT LCD 显示无符号整数
 *
 * @note    无
 * @param   x   起始横坐标
 * @param   y   起始纵坐标
 * @param   num 待显示的无符号整数
 * @param   len 待显示的无符号整数长度
 * @retval  无
 */
void HD_LCD_ShowUnsignedNum(uint16_t x, uint16_t y, uint32_t num, uint8_t len) {
    uint8_t showOfHead = 0;
    for (uint8_t i = 0; i < len; i++) {
        uint8_t temp = (num / HD_LCD_Pow(10, len - i - 1)) % 10;    // 取出所在位数字
        // 当前位数字不为 0，或者是数字的有效位
        if (temp != 0 || showOfHead != 0) {
            showOfHead = 1;
            HD_LCD_ShowChar(x + i * fontSize / 2, y, temp + '0');
            continue;
        }

        HD_LCD_ShowChar(x + i * fontSize / 2, y, '0');
    }
}

/**
 * @brief   TFT LCD 显示有符号整数
 *
 * @note    无
 * @param   x   起始横坐标
 * @param   y   起始纵坐标
 * @param   num 待显示的有符号整数
 * @param   len 待显示的有符号整数长度（符号位占用 1 个长度）
 * @retval  无
 */
void HD_LCD_ShowSignedNum(uint16_t x, uint16_t y, int32_t num, uint8_t len) {
    HD_LCD_ShowChar(x, y, num >= 0 ? '+' : '-');
    num = num > 0 ? num : -num;

    uint8_t showOfHead = 0;
    for (uint8_t i = 1; i < len; i++) {
        uint8_t temp = (num / HD_LCD_Pow(10, len - i - 1)) % 10;    // 取出所在位数字
        // 当前位数字不为 0，或者是数字的有效位
        if (temp != 0 || showOfHead != 0) {
            showOfHead = 1;
            HD_LCD_ShowChar(x + i * fontSize / 2, y, temp + '0');
            continue;
        }

        HD_LCD_ShowChar(x + i * fontSize / 2, y, '0');
    }
}

/**
 * @brief   TFT LCD 显示十六进制数字
 *
 * @note    无
 * @param   x   起始横坐标
 * @param   y   起始纵坐标
 * @param   num 待显示的十六进制数字
 * @param   len 待显示的十六进制数字长度（为字节数的2倍）
 * @retval  无
 */
void HD_LCD_ShowHexNum(uint16_t x, uint16_t y, uint32_t num, uint8_t len) {
    uint8_t showOfHead = 0;
    for (uint8_t i = 0; i < len; i++) {
        uint8_t temp = (num / HD_LCD_Pow(16, len - i - 1)) % 16;    // 取出所在位数字
        // 当前位数字不为 0，或者是数字的有效位
        if (temp != 0 || showOfHead != 0) {
            showOfHead = 1;
            HD_LCD_ShowChar(x + i * fontSize / 2, y, temp < 10 ? temp + '0' : temp - 10 + 'A');
            continue;
        }

        HD_LCD_ShowChar(x + i * fontSize / 2, y, '0');
    }
}

/**
 * @brief   TFT LCD 显示二进制数字
 *
 * @note    无
 * @param   x   起始横坐标
 * @param   y   起始纵坐标
 * @param   num 待显示的二进制数字
 * @param   len 待显示的二进制数字的 0 或 1 个数
 * @retval  无
 */
void HD_LCD_ShowBinNum(uint16_t x, uint16_t y, uint32_t num, uint8_t len) {
    uint8_t showOfHead = 0;
    for (uint8_t i = 0; i < len; i++) {
        uint8_t temp = (num / HD_LCD_Pow(2, len - i - 1)) % 2;  // 取出所在位数字
        // 当前位数字不为 0，或者是数字的有效位
        if (temp != 0 || showOfHead != 0) {
            showOfHead = 1;
            HD_LCD_ShowChar(x + i * fontSize / 2, y, temp + '0');
            continue;
        }

        HD_LCD_ShowChar(x + i * fontSize / 2, y, '0');          // 补 0 或空格
    }
}

/**
 * @brief   TFT LCD 显示字符串
 *
 * @note    无
 * @param   x   起始横坐标
 * @param   y   起始纵坐标
 * @param   num 待显示的浮点数
 * @param   len 待显示的浮点数长度
 * @retval  无
 */
void HD_LCD_ShowFloatNum(uint16_t x, uint16_t y, float num, uint8_t len) {
    const uint16_t numOfHundred = num * 100;
    for (uint8_t i = 0; i < len; i++) {
        uint8_t temp = (numOfHundred / HD_LCD_Pow(10, len - i - 1)) % 10;
        if (i == (len - 2)) {
            // 显示小数点
            HD_LCD_ShowChar(x + (len - 2) * fontSize / 2, y, '.');
            i++;
            len += 1;
        }

        HD_LCD_ShowChar(x + i * fontSize / 2, y, temp + '0');
    }
}

/** endregion 显示 ASCII */



/** region 绘制图形 */

/**
 * @brief   TFT LCD 画点
 *
 * @note    无
 * @param   x       起始横坐标
 * @param   y       起始纵坐标
 * @param   color   画点的颜色
 * @retval  无
 */
void HD_LCD_DrawPoint(uint16_t x, uint16_t y, uint16_t color) {
    // 设置光标位置
    HD_LCD_SetAddress(x, y, x, y);

    HD_LCD_WriteData16(color);
}

/**
 * @brief   TFT LCD 画线
 *
 * @note    原理：较长的方向步进为 1，较短的方向步进为 (x1-x)/(y1-y)
 * @param   x   起始横坐标
 * @param   y   起始纵坐标
 * @param   x1  结束横坐标
 * @param   y1  结束纵坐标
 * @param   color   填充颜色
 * @retval  无
 */
void HD_LCD_DrawLine(uint16_t x, uint16_t y, uint16_t x1, uint16_t y1, uint16_t color) {
    // 画线起点坐标
    int currX = x, currY = y;

    // 计算横坐标增量
    int deltaX = x1 > x ? x1 - x : x - x1;          // 横坐标增量
    int xIncDir = x1 > x ? 1 : (x1 == x ? 0 : -1);  // 横坐标增加方向：1，正向；0，垂直线；-1，反向
    // 计算纵坐标增量
    int deltaY = y1 > y ? y1 - y : y - y1;          // 纵坐标增量
    int yIncDir = y1 > y ? 1 : (y1 == y ? 0 : -1);  // 纵坐标增加方向：1，正向；0，垂直线；-1，反向

    // 选取基本增量坐标轴（选取较大的轴）
    int distance = deltaX > deltaY ? deltaX : deltaY;

    int tempX = 0, tempY = 0;
    for (int i = 0; i < distance + 1; i++) {
        HD_LCD_DrawPoint(currX, currY, color);// 画点

        tempX += deltaX;
        if (tempX > distance) {
            tempX -= distance;
            currX += xIncDir;
        }

        tempY += deltaY;
        if (tempY > distance) {
            tempY -= distance;
            currY += yIncDir;
        }
    }
}

/**
 * @brief   TFT LCD 画矩形
 *
 * @note    无
 * @param   x   起始横坐标
 * @param   y   起始纵坐标
 * @param   x1  结束横坐标
 * @param   y1  结束纵坐标
 * @param   color   填充颜色
 * @retval  无
 */
void HD_LCD_DrawRectangle(uint16_t x, uint16_t y, uint16_t x1, uint16_t y1, uint16_t color) {
    HD_LCD_DrawLine(x, y, x1, y, color);    // 画横线
    HD_LCD_DrawLine(x, y, x, y1, color);    // 画竖线
    HD_LCD_DrawLine(x, y1, x1, y1, color);   // 画横线
    HD_LCD_DrawLine(x1, y, x1, y1, color);   // 画竖线
}

/**
 * @brief   TFT LCD 画圆形
 *
 * @note    无
 * @param   x       圆心横坐标
 * @param   y       圆心纵坐标
 * @param   r       半径
 * @param   color   填充颜色
 * @retval  无
 */
void HD_LCD_DrawCircle(uint16_t x, uint16_t y, uint8_t r, uint16_t color) {
    int a = 0, b = r;
    while (a <= b) {
        HD_LCD_DrawPoint(x - b, y - a, color);  // 3
        HD_LCD_DrawPoint(x + b, y - a, color);  // 0
        HD_LCD_DrawPoint(x - a, y + b, color);  // 1
        HD_LCD_DrawPoint(x - a, y - b, color);  // 2
        HD_LCD_DrawPoint(x + b, y + a, color);  // 4
        HD_LCD_DrawPoint(x + a, y - b, color);  // 5
        HD_LCD_DrawPoint(x + a, y + b, color);  // 6
        HD_LCD_DrawPoint(x - b, y + a, color);  // 7
        a++;
        if ((a * a + b * b) > (r * r)) {    // 判断要画的点是否过远
            b--;
        }
    }
}

/** endregion 绘制图形 */

/** region 汉字 */

/** endregion 汉字显示 */

/** endregion Public Definition Region */


/** region Private Definition Region */

/**
 * @brief   TFT LCD 写 8 位寄存器
 *
 * @note
 * @param   dat 待写入的的 16 位数据
 * @retval  无
 */
void HD_LCD_WriteReg(uint8_t regAddr) {
    HD_LCD_DC(RESET);//写命令
    HD_LCD_WriteByte(regAddr);
    HD_LCD_DC(SET);//写数据
}

/**
 * @brief   TFT LCD 写 8 位数据
 *
 * @note    无
 * @param   无
 * @retval  无
 */
void HD_LCD_WriteData8(uint8_t dat) {
    HD_LCD_WriteByte(dat);
}

/**
 * @brief   TFT LCD 写 16 位数据
 *
 * @note    即使高 8 位为 0，依然需要写入一个字节
 * @param   dat 待写入的的 16 位数据
 * @retval  无
 */
void HD_LCD_WriteData16(uint16_t dat) {
    HD_LCD_WriteByte(dat >> 8);
    HD_LCD_WriteByte(dat);
}

/**
 * @brief   TFT LCD 设置要显示的地址
 *
 * @note
 * @param   x   起始横坐标
 * @param   y   起始纵坐标
 * @param   x1  结束横坐标
 * @param   y1  结束纵坐标
 * @retval  无
 */
void HD_LCD_SetAddress(uint16_t x, uint16_t y, uint16_t x1, uint16_t y1) {
    if (USE_HORIZONTAL == 0) {
        HD_LCD_WriteReg(0x2a);  // 列地址设置
        HD_LCD_WriteData16(x + 26);
        HD_LCD_WriteData16(x1 + 26);
        HD_LCD_WriteReg(0x2b);  // 行地址设置
        HD_LCD_WriteData16(y + 1);
        HD_LCD_WriteData16(y1 + 1);
        HD_LCD_WriteReg(0x2c);  // 储存器写
    } else if (USE_HORIZONTAL == 1) {
        HD_LCD_WriteReg(0x2a);  // 列地址设置
        HD_LCD_WriteData16(x + 26);
        HD_LCD_WriteData16(x1 + 26);
        HD_LCD_WriteReg(0x2b);  // 行地址设置
        HD_LCD_WriteData16(y + 1);
        HD_LCD_WriteData16(y1 + 1);
        HD_LCD_WriteReg(0x2c);  // 储存器写
    } else if (USE_HORIZONTAL == 2) {
        HD_LCD_WriteReg(0x2a);  // 列地址设置
        HD_LCD_WriteData16(x + 1);
        HD_LCD_WriteData16(x1 + 1);
        HD_LCD_WriteReg(0x2b);  // 行地址设置
        HD_LCD_WriteData16(y + 26);
        HD_LCD_WriteData16(y1 + 26);
        HD_LCD_WriteReg(0x2c);  // 储存器写
    } else {
        HD_LCD_WriteReg(0x2a);  // 列地址设置
        HD_LCD_WriteData16(x + 1);
        HD_LCD_WriteData16(x1 + 1);
        HD_LCD_WriteReg(0x2b);  // 行地址设置
        HD_LCD_WriteData16(y + 26);
        HD_LCD_WriteData16(y1 + 26);
        HD_LCD_WriteReg(0x2c);  // 储存器写
    }
}

/**
 * @brief   TFT LCD 写入一个字节
 *
 * @note    SPI 的模式选择时钟极性（CPOL）为 1，时钟相位（CPHA）为 0。
 * @param   dat  待写入数据
 * @retval  无
 */
void HD_LCD_WriteByte(uint8_t dat) {
    HD_LCD_CS(RESET);           // 片选使能
    for (uint8_t j = 0; j < 8; j++) {
        HD_LCD_SCK(RESET);
        HD_LCD_SDA((FlagStatus)(dat & (0x80 >> j)));
        HD_LCD_SCK(SET);
    }
    HD_LCD_CS(SET);
}

void HD_LCD_WriteBit(GPIO_TypeDef *GPIOx, uint16_t Pin, FlagStatus val) {
    if (val == RESET) {
        GPIOx->BRR = Pin;
    } else {
        GPIOx->BSRR = Pin;
    }
}

uint32_t HD_LCD_Pow(uint8_t m, uint8_t n) {
    uint32_t result = 1;
    while (n--)result *= m;
    return result;
}

uint8_t HD_LCD_GetASCII(uint8_t x, uint16_t y) {
    switch (fontSize) {
        case 12:
            return ASCII_12x06[x][y];
        case 16:
            return ASCII_16x08[x][y];
        case 24:
            return ASCII_24x12[x][y];
        case 32:
            return ascii_32x16[x][y];
        default:
            return 0x00;
    }
}

/** endregion Private Definition Region */

